/**
 * Covalent Server Code File
 * 
 * File:        protocol.c
 * Brief:       BRIEF
 * Author:      Chen Kangbing
 * Email:       ckblau@outlook.com
 * Modified:    2021/10/09 09:28:56
 *
 */


#include "protocol.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define METHOD_NUM 3
const MethodMappingTypeDef METHODS[METHOD_NUM] = {
    {"HEAD", HTTP_HEAD},
    {"GET", HTTP_GET},
    {"POST", HTTP_POST}
};

#define STATUS_NUM 5
const StatusMappingTypeDef STATUS[STATUS_NUM] = {
    {200, "OK"},
    {400, "Bad Request"},
    {403, "Forbidden"},
    {404, "Not Found"},
    {500, "Internal Server Error"}
};


char *find_splitter(char *key, char* end) {
    for (char *now = key; now < end; ++now) {
        if (*now == ' ') return now;
        if (now < end - 1 && now[0] == '\r' && now[1] == '\n') return NULL;
    }
    return NULL;
}

char *find_line_end(char *key, char* end) {
    for (char *now = key; now < end - 1; ++now) {
        if (now[0] == '\r' && now[1] == '\n') return now;
    }
    return end;
}

int protocol_parse(TransactionTypeDef *ptrans, WorkerThreadTypeDef *pworker) {
    if (!ptrans || !pworker) return 1;

    char *eop = pworker->down_buffer + pworker->down_buffer_size;
    char *method = pworker->down_buffer;
    char *line2 = find_line_end(method, eop);
    if (line2 == eop) return 2;
    char *method_e = find_splitter(method, line2);
    if (!method_e || method_e == line2) return 3;
    char *path = method_e + 1;
    char *path_e = find_splitter(path, line2);
    if (!path_e || path_e == line2) return 4;

    int line1_size = line2 - method;
    pworker->log_req_header = malloc(line1_size + 1);
    strncpy(pworker->log_req_header, method, line1_size + 1);
    pworker->log_req_header[line1_size] = 0;

    char *line = line2;
    char *body = NULL;
    while (line != eop) {
        char *nextline = find_line_end(line + 2, eop);
        if (nextline - line == 2) {
            body = nextline + 2;
            break;
        }
        line = nextline;
    }
    if (!body) return 5;

    *method_e = 0;
    // printf("Method: %s\n", method);
    ptrans->method = HTTP_NULL;
    for (int i = 0; i < METHOD_NUM; ++i) {
        if (!strcasecmp(method, METHODS[i].name)) {
            ptrans->method = METHODS[i].value;
        }
    }
    if (ptrans->method == HTTP_NULL) return 6;

    *path_e = 0;
    // printf("Path: %s\n", path);
    ptrans->path = path;
    ptrans->path_len = path_e - path;

    // printf("Body: %s\n", body);
    ptrans->req_body = body;
    ptrans->req_body_size = eop - body;

    return 0;
}

int protocol_pack(TransactionTypeDef *ptrans, WorkerThreadTypeDef *pworker) {
    if (!ptrans || !pworker) return -1;

    pworker->up_buffer = malloc(ptrans->rsp_header_size + ptrans->rsp_body_size + 30);
    char *cur = pworker->up_buffer;

    const char *reason = NULL;
    for (int i = 0; i < STATUS_NUM; ++i) {
        if (ptrans->status == STATUS[i].value) {
            reason = STATUS[i].reason;
        }
    }
    if (!reason) return -2;
    cur += sprintf(cur, "HTTP/1.1 %d %s\r\n", ptrans->status, reason);

    if (ptrans->rsp_header) 
        cur += sprintf(cur, "%s\r\n", ptrans->rsp_header);
    cur += sprintf(cur, "\r\n");

    if (ptrans->rsp_body) 
        memcpy(cur, ptrans->rsp_body, ptrans->rsp_body_size);
    
    pworker->up_buffer_size = (cur - pworker->up_buffer) + ptrans->rsp_body_size;

    return 0;
}


